/*
 * ServerSocket
*/
#ifndef MAGUS_SERVER_SOCKET_HPP
#define MAGUS_SERVER_SOCKET_HPP

#include <magus/config.hpp>
#include <magus/network/Socket.hpp>        
#include <assert.h>
#include <string>        

#ifdef __MAGUS_WIN32                                                                                                                    
#   include <winsock2.h>    
    typedef SOCKET sockdes_t;
    
#elif defined(__MAGUS_POSIX)   
#ifndef _REENTRANT
#define _REENTRANT
#endif
#   include <unistd.h>
#   include <netdb.h>
#   include <sys/types.h>
#   include <sys/socket.h>
#   include <sys/time.h>
#   include <netinet/in.h>
#   include <netinet/tcp.h>
#   include <arpa/inet.h>
#   include <errno.h>
#   include <assert.h>
#   define INVALID_SOCKET -1
#   define SOCKET_ERROR -1
    typedef int sockdes_t;
#endif

namespace magus {

/*
 * ServerSocket Definition
*/
class ServerSocket
{    
public: 
    ServerSocket()
        : _M_fd(INVALID_SOCKET), _M_port(0) 
    { 
    #ifdef __MAGUS_WIN32
        WSADATA wsaData;
        WSAStartup(MAKEWORD(2,2), &wsaData);
    #endif
    } 
    
    ~ServerSocket()
    {   
        Close();
    #ifdef __MAGUS_WIN32
            WSACleanup();
    #endif
    }

    void Close()
    { 
        if (_M_fd != INVALID_SOCKET) 
        {
    #ifdef __MAGUS_WIN32
            closesocket(_M_fd);
    #elif defined(__MAGUS_POSIX)
            close(_M_fd);
    #endif
            _M_fd = INVALID_SOCKET;
        }
    } 

    // create a server socket with port and backlog
    int Create(int port, int backlog = 5)   
    {
        assert(_M_fd == INVALID_SOCKET);
        _M_port = port;
        _M_family = AF_INET;
        _M_fd = socket(AF_INET, SOCK_STREAM, 0);
        if (_M_fd == INVALID_SOCKET) 
        {
            return -1;
        }
        int val = 1;
        int len = sizeof(val);
        setsockopt(_M_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&val, len);
        _M_sa.sin_family = AF_INET;
        _M_sa.sin_addr.s_addr = INADDR_ANY;
        _M_sa.sin_port = htons((unsigned short)port);
        if (bind(_M_fd, (sockaddr *)&_M_sa, sizeof(_M_sa)) == SOCKET_ERROR
             || listen(_M_fd, backlog) == SOCKET_ERROR) 
        {
            return -1;
        }
        return 0;   
    }
    
    // listen for client to connect
    int Accept(socket_t& s)
    {
        //assert(_M_fd != INVALID_SOCKET);
        // NO WSAEWOULDBLOCK for blocking socket
        socklen_t len = sizeof(s.sa);
        s.fd = accept(_M_fd, (struct sockaddr *)&(s.sa), &len);
        if (s.fd == INVALID_SOCKET) {
    #ifdef __MAGUS_WIN32                                                                                                                    
            return WSAGetLastError();
    #else
            return errno;
    #endif  
        }
        s.port = 0;
        s.family = _M_family;
        return 0;
    }   
        
private:
    sockdes_t           _M_fd;
    int                 _M_port;
    int                 _M_family;
    struct sockaddr_in  _M_sa;

};  // class ServerSocket

} // namespace magus

// Change Log:
//    02 JLY 03  Initial version.

#endif  /* MAGUS_SERVER_SOCKET_HPP */

